__version__ = "$Revision: 127 $"
__date__ = "$Date: 2011-07-31 22:12:28 +0200 (So, 31 Jul 2011) $"
__lastChangedBy__ = "$Author: wolters@3sconsult.de $"
__repository__ = "$HeadURL: https://sir3spy.googlecode.com/svn/Sir3sPy/Utils/trunk/Db.py $"

"""Module Opc.

Description
-----------
    - Class(es): 
        -- OpcClient
"""

import sys
import os
import logging
from optparse import OptionParser,OptionGroup 

from Sir3sPy.Utils import OpenOPC3

class OpcClient:
    """Typical OPC Client tasks.
    """    
              
    def __init__(self,server='Matrikon.OPC.Simulation.1',host='localhost',com='Matrikon.OPC.Automation.1'):
        """OPC <server> at <host> will be used through COM-object <com>.   
        """           

        fileName =__file__                
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.__init__'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start')
 
        try:
            if isinstance(com,str):
                self.com=com                                         
            logger.debug('COM to be used: {:s}'.format(self.com))
            self.opc = OpenOPC3.client(self.com)
            logger.debug('COM {:s} ok'.format(self.com))            
            logger.debug('Servers available: {:s}'.format(self.availableServers()))
            
            if isinstance(server,str):
                self.server=server                                         
            logger.debug('Server to be connected: {:s}'.format(self.server))                  
            if isinstance(host,str):
                self.host=host                                         
            logger.debug('Host: {:s}'.format(self.host))                                              
        except:
            logger.error('Exception!')                 
            raise
        finally:
            logger.debug('_Done')                                       

    def availableServers(self):
        """Returns a list of all available Servers.                                             
        """

        fileName =__file__
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.availableServers'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start') 
        
        returnValue=None        
        try: 
            returnValue=self.opc.servers()                      
        except:
            logger.error('Exception!')
            raise   
        finally:
            logger.debug('_Done') 
            return returnValue    

    def connect(self):
        """Connect.                                             
        """

        fileName =__file__
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.connect'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start') 
        
        returnValue=None        
        try: 
            self.opc.connect(opc_server=self.server,opc_host=self.host)            
            logger.debug('SERVER {:s} at {:s} connection ok'.format(self.server,self.host))                                               
        except:
            logger.error('Exception!')
            raise   
        finally:
            logger.debug('_Done') 
            return returnValue    

    def disconnect(self):
        """Disconnect.                                             
        """

        fileName =__file__
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.disconnect'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start') 
            
        try: 
            self.opc._opc.Disconnect()                                                                      
        except:
            logger.error('Exception!')
            raise   
        finally:
            logger.debug('_Done')               
        
    def availableItems(self):
        """Returns a list of all available OPC Items.                                             
        """

        fileName =__file__
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.availableItems'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start') 
        
        returnValue=None        
        try: 
            # list(self, paths='*', recursive=False, flat=False, include_type=False):
            returnValue=self.opc.list(paths='*',recursive=True)                      
        except:
            logger.error('Exception!')
            raise   
        finally:
            logger.debug('_Done') 
            return returnValue                  

    def readItems(self, items=None):
        """Returns list of (item, value, quality, time) for the specified items.
        """

        fileName =__file__
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.readItems'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start') 
        
        returnValue=None        
        try: 
            # read(self, tags=None, group=None, size=None, pause=0, source='hybrid', update=-1, timeout=5000, sync=False, include_error=False, rebuild=False)
            returnValue= self.opc.read(items,sync=True)   
            for (item, value, quality, time) in returnValue:
                logger.debug('item: {:s} value: {:s} quality: {:s} time: {:s}'.format(item,str(value),str(quality),str(time)))                   
        except:
            logger.error('Exception!')
            raise   
        finally:
            logger.debug('_Done') 
            return returnValue  
        
    def writeItems(self, itemvaluepairs=None):
        """Write the specified list of (item, value) pair(s). Returns list of (item, writestatus).
        """

        fileName =__file__
        logName=fileName[__file__.find('Sir3sPy'):]+'.'+self.__class__.__name__+'.writeItems'
        logName=logName.replace('.py','').replace('\\','.').replace('/','.')  
        logger = logging.getLogger(logName)              
        logger.debug('Start') 
        
        returnValue=None        
        try: 
            # write(self, tag_value_pairs, size=None, pause=0, include_error=False):
            returnValue= self.opc.write(itemvaluepairs)    
            for i,(item, writestatus) in enumerate(returnValue):
                (item,value)=itemvaluepairs[i]
                logger.debug('item: {:s} writestatus: {:s} value: {:s}'.format(item,str(writestatus),str(value)))                                
        except:
            logger.error('Exception!')
            raise   
        finally:
            logger.debug('_Done') 
            return returnValue          

if __name__ == "__main__":

    # logging configuration
    filename=__file__
    loggerName=filename[__file__.find('Sir3sPy'):].replace('.py','').replace('\\','.').replace('/','.')
    logging.getLogger(loggerName).addHandler(logging.NullHandler())
    # configure logfile
    moduleName,ext=os.path.splitext(os.path.basename(__file__))     
    logFileName = os.path.join(os.getcwd(),moduleName+'.log') 
    # configure logging       
    logging.basicConfig(filename=logFileName,filemode='w',level=logging.WARNING,format="%(asctime)s ; %(name)-60s ; %(levelname)-7s ; %(message)s")    
        
    try:
        # logging                            
        logger = logging.getLogger(loggerName)

        # default return value
        returnValue=None
        
        # configure command line options ...
        parser = OptionParser(usage="%prog <Database> [-v|-q] [-s SERVER] [-h HOST] [-c COM] [-i ITEMSANDVALUES]")
        
        optGrpLog = OptionGroup(parser,"Log Options")
        optGrpLog.add_option("-v", action="store_true" , dest="verbose", default=True, help="DEBUG log level")
        optGrpLog.add_option("-q", action="store_false", dest="verbose",help="WARNING log level")    
        parser.add_option_group(optGrpLog)
        
        optGrpSrv = OptionGroup(parser, "Server Options")       
        optGrpSrv.add_option("-s", "--server", help="Matrikon.OPC.Simulation.1"
                      ,action="store", type="string", dest="server", metavar="SERVER")        
        optGrpSrv.add_option("-n", "--host", help="localhost (name or IP)"
                      ,action="store", type="string", dest="host", metavar="HOST")        
        optGrpSrv.add_option("-c", "--com", help="Matrikon.OPC.Automation.1 (the name of the COM object)"
                      ,action="store", type="string", dest="com", metavar="COM")  
        parser.add_option_group(optGrpSrv)

        optGrpItems = OptionGroup(parser, "Items")        
        optGrpItems.add_option("-i", "--items", help='"'+"{'Actor.aActor':0,'Sensor.aSensor':0}"+'"'+' (i.e. Actor.aActor is the OPC item and 0 is the value) the items are read, written and read again'
                      ,action="store", type="string", dest="items", metavar="ITEMS") 
        parser.add_option_group(optGrpItems) 
                
        # parse command line
        (options, argsLeftOver) = parser.parse_args()

        # configure logging from command line
        if options.verbose:                        
            logger.setLevel(logging.DEBUG)
        else:
            logger.setLevel(logging.WARNING)    
        
        # check command line
        if len(argsLeftOver)>0:
            print ('option parse: keyless arguments are not defined')
            print ('usage information: '+os.path.basename(sys.argv[0])+' '+'--help')     
            returnValue=-2       
            sys.exit(returnValue)   
                            
        if options.server==None:
            server='Matrikon.OPC.Simulation.1'            

        if options.host==None:
            host='localhost'                

        if options.com==None:
            com='Matrikon.OPC.Automation.1'       
        
        opc=OpcClient()
        opc.connect()            
        if options.items==None: 
            returnValue=1 
            sys.exit(returnValue)
        else:                
            p=eval(options.items)
            items=list(p.keys())
            itemvaluepairs=list(p.items())                       
            for (item, value, quality, timestamp) in opc.readItems(items):
                print(item,value,quality,timestamp)
            for (item, writestatus) in opc.writeItems(itemvaluepairs):
                print(item,writestatus)                
            for (item, value, quality, timestamp) in opc.readItems(items):
                print(item,value,quality,timestamp)        
        opc.disconnect()
        returnValue=1
    except SystemExit: 
        pass    
    except:
        returnValue=-1
        logger.error('Exception!') 
    finally:                                         
        sys.exit(returnValue)                

